package jdbc;

import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @author liuziyang
 * @create 2018-07-30 20:56
 */
public class DaoImpl<T> implements Dao<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DaoImpl.class);

    @Override
    public void update(String sql, Object... args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = JdbcUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            preparedStatement.executeUpdate();
        } catch (Exception e) {
            LOGGER.error("error", e);
        } finally {
            JdbcUtils.release(connection, preparedStatement);
        }
    }

    @Override
    public T get(Class<T> clazz, String sql, Object... args) {
        T entity = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultset = null;

        try {
            //1. 获取connection
            connection = JdbcUtils.getConnection();
            //2.获取preparedStatement
            preparedStatement = connection.prepareStatement(sql);
            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            //4.获取resultSet
            resultset = preparedStatement.executeQuery();
            //5.处理ResultSet，把指针向下移动一个单位
            if (resultset.next()) {
                //6.若ResultSet中有记录，准备一个Map<string, Object>对象，键存放列的别名，值存放列的值
                Map<String, Object> values = new HashMap<>();
                //7.得到ResultSetMateData对象
                ResultSetMetaData rsmd = resultset.getMetaData();

                //8.由ResultSetMateData对象得到结果中有多少列
                int columnCount = rsmd.getColumnCount();
                //9.由ResultSetMateData得到每一列的别名，有ResultSet得到具体每一列的值
                for (int i = 1; i <= columnCount; i++) {
                    String colName = rsmd.getColumnLabel(i);
                    Object colValue = resultset.getObject(colName);
                    //10.填充Map对象
                    values.put(colName, colValue);
                }
                //11.用反射创建Class对应的对象
                entity = clazz.newInstance();
                //12.遍历Map对象，用反射填充对象的属性值，
                // 属性名为Map中的Key，属性值为Map中的Value
                for (Map.Entry<String, Object> entry : values.entrySet()) {
                    BeanUtils.setProperty(entity, entry.getKey(), entry.getValue());
                }
            }
        } catch (Exception e) {
            LOGGER.error("error", e);
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultset);
        }

        return entity;
    }

    @Override
    public List<T> getForList(Class<T> clazz, String sql, Object... args) {
        List<T> entityList = new ArrayList<>();

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultset = null;

        try {
            //1. 获取connection
            connection = JdbcUtils.getConnection();
            //2.获取preparedStatement
            preparedStatement = connection.prepareStatement(sql);
            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            //4.获取resultSet
            resultset = preparedStatement.executeQuery();

            List<String> columnLabels = getColumnLabels(resultset);

            //5.处理ResultSet，把指针向下移动一个单位
            while (resultset.next()) {
                //6.若ResultSet中有记录，准备一个Map<string, Object>对象，键存放列的别名，值存放列的值
                Map<String, Object> values = new HashMap<>();
                //9.由ResultSetMateData得到每一列的别名，有ResultSet得到具体每一列的值
                for (String colName : columnLabels) {
                    //10.填充Map对象
                    values.put(colName, resultset.getObject(colName));
                }

                //11.用反射创建Class对应的对象
                T entity = clazz.newInstance();
                //12.遍历Map对象，用反射填充对象的属性值，
                // 属性名为Map中的Key，属性值为Map中的Value
                for (Map.Entry<String, Object> entry : values.entrySet()) {
                    BeanUtils.setProperty(entity, entry.getKey(), entry.getValue());
                }
                //13.将实例放入到结果集合中
                entityList.add(entity);
            }
        } catch (Exception e) {
            LOGGER.error("error", e);
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultset);
        }

        return entityList;
    }


    @SuppressWarnings("unchecked")
    @Override
    public <E> E getForValue(String sql, Object... args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultset = null;

        E result = null;

        try {
            connection = JdbcUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            // 1. 取出的结果集应该只有一行，且只有一列
            resultset = preparedStatement.executeQuery();

            // 2.取得结果集中的数据
            if (resultset.next()) {
                result = (E) resultset.getObject(1);
            }

        } catch (Exception e) {
            LOGGER.error("error", e);
        } finally {
            JdbcUtils.release(connection, preparedStatement, resultset);
        }

        return result;
    }

    /**
     * 获取结果集ColumnLabel
     *
     * @param resultSet
     * @return
     * @throws SQLException
     */
    private List<String> getColumnLabels(ResultSet resultSet) throws SQLException {
        List<String> labels = new ArrayList<>();

        //7.得到ResultSetMateData对象
        ResultSetMetaData rsmd = resultSet.getMetaData();
        //8.由ResultSetMateData对象得到结果中有多少列
        int columnCount = rsmd.getColumnCount();
        for (int i = 0; i < columnCount; i++) {
            labels.add(rsmd.getColumnLabel(i + 1));
        }
        return labels;
    }
}
